<template>
  <div :class="!isModal ? 'w-full fixed top-0 bg-gray-50' : ''">
    <a-page-header
      v-if="!isModal"
      title="编辑流程"
      sub-title="添加节点：鼠标悬停在左侧节点上，长按鼠标左键，可将节点拖入进画布"
      @back="() => $router.go(-1)"
    >
      <template #backIcon>
        <a-button>
          <template #icon
            ><Icon size="18" style="vertical-align: top"><ChevronDoubleLeft16Filled /></Icon
          ></template>
          返回列表
        </a-button>
      </template>
    </a-page-header>
    <div class="flowCanvas" :style="{ height: isModal ? 'calc(100vh - 116px)' : 'calc(100vh - 80px)' }">
      <div ref="container" class="container"></div>
      <!-- 节点面板 -->
      <BpmnNodePanel v-if="lf" ref="BpmnNodePanel" :lf="lf"></BpmnNodePanel>
      <!-- 数据面板 -->
      <BpmnDataPanel v-if="lf" ref="BpmnDataPanel" :lf="lf"></BpmnDataPanel>
      <!-- 弹窗编辑 -->
      <a-card
        :title="title"
        :style="{
          'margin-left': '10px',
          width: '400px',
          height: 'calc(100% - 0px)',
          'box-shadow': '0px 3px 5px 1px #e0e0e0'
        }"
        :body-style="{ height: 'calc(100% - 110px)', 'overflow-y': 'auto', 'overflow-x': 'hidden' }"
      >
        <a-spin :spinning="!renderAble">
          <component
            :is="nodeDrawer"
            v-if="renderAble"
            ref="nodeForm"
            :current-node="currentNode"
            :lf="lf"
            :is-modal="isModal"
            :hide-sign-setting="hideSignSetting"
            :is-disabled-flow-type="isDisabledFlowType"
            @close="saveNode"
          ></component>
          <IcpEmpty v-else />
        </a-spin>
      </a-card>
      <div class="btn-content">
        <div class="float-right btn-group">
          <a-button v-if="!isModal" type="primary" :loading="commitLoading" @click="submit(true)">暂存</a-button>
          <a-button class="ant-btn-success" :loading="commitLoading" @click="submit(false)">提交并启用</a-button>
        </div>
        <div class="clear-both"></div>
      </div>
    </div>
  </div>

  <!--错误信息-->
  <a-modal v-model:visible="errorVisible" title="节点错误信息" :footer="null" :mask-closable="false" :keyboard="false">
    <p v-for="(item, index) in errMsgList" :key="index">
      <Icon size="24" color="#f00" class="close-icon"><CloseCircleSharp /></Icon>
      【{{ item.nodeName }}】
      {{ item.errorMsg }}
    </p>
  </a-modal>
</template>

<script>
import LogicFlow from '@logicflow/core'
import '@logicflow/core/dist/style/index.css'
import {
  DndPanel,
  BpmnElement,
  BpmnXmlAdapter,
  BpmnAdapter,
  Snapshot,
  // Control,
  Menu,
  SelectionSelect
} from '@logicflow/extension'
import '@logicflow/extension/lib/style/index.css'
import BpmnNodePanel from './components/BpmnNodePanel.vue'
import BpmnDataPanel from './components/BpnmDataPanel.vue'
import ApprovalNode from './components/nodeForm/approvalNode.vue'
import ConditionNode from './components/nodeForm/conditionNode.vue'
import WorkflowForm from './components/nodeForm/workflowForm.vue'
import lineForm from './components/nodeForm/lineForm.vue'
import sendNode from './components/nodeForm/sendNode.vue'
import startHtml from './registerNode/start/CustomHtml'
import userTaskHtml from './registerNode/userTask/CustomHtml'
import sendTaskHtml from './registerNode/sendTask/CustomHtml'
import endHtml from './registerNode/end/CustomHtml'
import selectTask from './registerNode/selectTask/CustomHtml'
import conditionTask from './registerNode/conditionTask/CustomHtml'

import { randomRange } from '@@/utils/index'
import { saveFlow, getDetails, getCtmsDetails, update, updateCtms } from '@@/api/flow/index'
import { useRoute, useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import { CloseCircleSharp } from '@vicons/ionicons5'
import { cloneDeep } from 'lodash'
import { ChevronDoubleLeft16Filled } from '@vicons/fluent'
class canvas {
  constructor(properties = {}) {
    this.data = {
      id: 0,
      properties,
      type: 'canvas'
    }
  }
}
export default {
  name: 'LogicflowBpmn',
  components: {
    BpmnNodePanel,
    DndPanel,
    BpmnDataPanel,
    ApprovalNode,
    ConditionNode,
    WorkflowForm,
    lineForm,
    sendNode,
    CloseCircleSharp,
    ChevronDoubleLeft16Filled
  },
  props: {
    taskFlowId: {
      type: String,
      default: ''
    },
    taskEntryType: {
      type: Number,
      default: 4
    },
    isModal: {
      type: Boolean,
      default: false
    },
    hideSignSetting: {
      type: Boolean,
      default: false
    },
    customPath: {
      type: String,
      default: ''
    },
    isDisabledFlowType: {
      type: Boolean,
      default: false
    }
  },
  emits: ['updateFlow'],
  data() {
    return {
      lf: null,
      currentNode: new canvas(),
      title: '编辑流程信息',
      flowForm: {},
      nodeDrawer: 'WorkflowForm',
      renderAble: false,
      id: useRoute().query.id,
      entryType: useRoute().query.entryType,
      router: useRouter(),
      commitLoading: false,
      errorVisible: false,
      errMsgList: [],
      isConfig: useRoute().query.isConfig === 'true',
      oldGetwayData: null
    }
  },
  mounted() {
    if (this.isModal) {
      this.id = this.taskFlowId
      this.entryType = this.taskEntryType
    }
    const data = {
      nodes: [
        {
          id: 'Flow_0000001',
          type: 'startEvent',
          x: 220,
          y: 220,
          properties: {
            name: '开始节点'
          }
        },
        {
          id: 'Flow_0000002',
          type: 'userTask',
          x: 450,
          y: 220,
          properties: {
            name: '发起节点',
            'flowable:dataType': 'dynamic',
            'flowable:userType': 'assignee',
            'flowable:assigneeName': '',
            'flowable:assignee': '${initiator}'
          }
        },
        {
          id: 'Flow_0000003',
          type: 'endEvent',
          x: 1110,
          y: 220,
          properties: {
            name: '结束节点'
          }
        }
      ],
      edges: [
        {
          id: 'Flow_0000004',
          type: 'bpmn:sequenceFlow',
          sourceNodeId: 'Flow_0000001',
          targetNodeId: 'Flow_0000002',
          startPoint: {
            x: 305,
            y: 220
          },
          endPoint: {
            x: 365,
            y: 220
          },
          properties: {},
          pointsList: [
            {
              x: 305,
              y: 220
            },
            {
              x: 365,
              y: 220
            }
          ]
        }
      ]
    }
    LogicFlow.use(BpmnElement)
    LogicFlow.use(DndPanel)
    LogicFlow.use(Snapshot)
    // LogicFlow.use(Control)
    LogicFlow.use(Menu)
    LogicFlow.use(SelectionSelect)
    LogicFlow.use(BpmnAdapter)

    //初始化
    this.lf = new LogicFlow({
      container: this.$refs.container,
      grid: {
        size: 20,
        visible: true,
        type: 'mesh',
        config: {
          color: '#f0f0f0',
          thickness: 1
        }
      },
      keyboard: {
        enabled: true
      },
      edgeTextEdit: false // 禁用边编辑
    })
    this.lf.register(userTaskHtml)
    this.lf.register(selectTask)
    this.lf.register(conditionTask)
    this.lf.register(sendTaskHtml)
    this.lf.register(startHtml)
    this.lf.register(endHtml)
    if (this.id) {
      this.getDetails()
    } else {
      this.renderAble = true
      this.lf.renderRawData(data)
    }
    //绑定事件
    const { eventCenter } = this.lf.graphModel
    this.bindEvent(eventCenter)
  },

  methods: {
    getDetails() {
      const params = {
        id: this.id,
        entryType: this.entryType || 1
      }
      const fn = this.entryType === 4 ? getCtmsDetails : getDetails
      fn(params).then((res) => {
        const data = JSON.parse(res.canvas)
        delete res.canvas
        this.currentNode = new canvas(res)
        this.flowForm = res
        this.renderAble = true
        this.lf.renderRawData(data)
      })
    },
    //校验是否添加/修改成功
    check(data) {
      if (data.hasOwnProperty('id')) {
        return true
      }
      this.errMsgList = data.list
      this.errorVisible = true
      return false
    },
    //保存流程
    submit(flag = false) {
      this.commitLoading = true
      this.errMsgList = []
      this.$refs.nodeForm.close()
      const { xmlData, drawData } = this.$refs.BpmnDataPanel.downloadXml()
      const params = Object.assign({ xml: xmlData, canvas: drawData }, this.flowForm)
      // console.log(params)
      // console.log('xmlData:', xmlData)
      if (!flag) {
        const graphModel = this.lf.graphModel
        try {
          const userTask = graphModel.nodes.find((item) => item.type === 'userTask' && item.id !== 'Flow_0000002')
          if (!userTask) {
            throw new Error('至少需要添加一个审批节点')
          }
          const conditionTask = graphModel.nodes.filter((item) => item.type === 'exclusiveGateway')
          if (conditionTask.length > 1) {
            throw new Error('最多只能添加一个条件流转节点')
          }
          graphModel.nodes.forEach((item) => {
            if (item.type === 'startEvent') {
              const len = graphModel.getNodeEdges(item.id).length
              if (len < 1) {
                throw new Error('开始节点必须连接其他节点')
              }
            }
            if (item.type === 'userTask') {
              const len = graphModel.getNodeEdges(item.id).length
              if (len < 2) {
                throw new Error('审批节点必须有连入和连出')
              }
            }
            if (item.type === 'exclusiveGateway') {
              const edges = graphModel.getNodeEdges(item.id)
              const len = edges.length
              if (len < 2) {
                throw new Error('条件节点必须有连入和连出')
              }
              const outEdges = graphModel.getNodeOutgoingEdge(item.id)
              let edgeFlag = false
              outEdges.forEach((edge) => {
                if (!edge.properties?.conditionExpression) {
                  edge.updateText('请配置')
                  edgeFlag = true
                } else {
                  const reg = /[&<>"]/
                  const name = edge.properties?.name
                  if (name) {
                    if (reg.test(name)) {
                      throw new Error('连接线的流条件名称不能包含特殊字符：&、<、>、"')
                    }
                  }
                }
              })
              if (edgeFlag) {
                throw new Error('请配置连接线的流条件')
              }
            }
            if (item.type === 'endEvent') {
              const len = graphModel.getNodeEdges(item.id).length
              if (len < 1) {
                throw new Error('结束节点必须被其他节点连接')
              }
            }
          })
        } catch (e) {
          message.error(e.message)
          this.commitLoading = false
          return
        }
      }
      const path = this.isConfig ? 'configure' : 'sop'
      if (this.id) {
        //修改流程
        params.id = this.id
        params.status = flag ? 0 : 1
        params.entryType = this.entryType || 1
        const fn = this.entryType === 4 ? updateCtms : update
        fn(params)
          .then((res) => {
            this.commitLoading = false
            if (!res || res.length === 0) {
              if (this.isModal) {
                this.$emit('updateFlow', true)
                return
              }
              message.success('操作成功！')
              if (this.customPath) {
                window.location.href = this.customPath
              } else {
                this.router.push({ path: `/${path}/flow/index` })
              }
            } else {
              this.errMsgList = res
              this.errorVisible = true
            }
          })
          .catch(() => {
            this.commitLoading = false
          })
      } else {
        //新增流程
        params.status = flag ? 0 : 1
        saveFlow(params)
          .then((res) => {
            this.commitLoading = false
            if (this.check(res)) {
              message.success('操作成功！')
              if (this.customPath) {
                window.location.href = this.customPath
              } else {
                this.router.push({ path: `/${path}/flow/index` })
              }
            }
          })
          .catch(() => {
            this.commitLoading = false
          })
      }
    },
    // 获取用户列表
    // addNode() {
    //   this.$refs.BpmnNodePanel.addUserTaskNode()
    // },
    // addSendNode() {
    //   this.$refs.BpmnNodePanel.addSendTaskNode()
    // },

    // 流程的事件注册
    bindEvent(eventCenter) {
      // 点击节点
      eventCenter.on('node:click', (args) => {
        // 开始节点，结束节点，发起节点不支持修改
        if (['startEvent', 'endEvent'].includes(args.data.type) || args.data.properties.name === '发起节点') {
          this.currentNode = new canvas(this.flowForm)
          this.title = '编辑流程信息'
          this.nodeDrawer = 'WorkflowForm'
          return
        }
        if (this.currentNode.data.id === args.data.id) {
          return
        }
        args.data.properties = this.restoreData(args.data.properties)
        this.currentNode = args
        this.nodeDrawer = 'ApprovalNode'
        if (args.data.type === 'sendTask') {
          this.title = '编辑抄送节点'
          this.nodeDrawer = 'sendNode'
        } else if (args.data.type === 'exclusiveGateway') {
          this.title = '编辑条件判断'
          this.oldGetwayData = cloneDeep(args)
          this.nodeDrawer = 'ConditionNode'
        } else {
          this.title = '编辑审批节点'
          this.nodeDrawer = 'ApprovalNode'
        }
      })

      // 点击画布
      eventCenter.on('blank:click', (args) => {
        this.currentNode = new canvas(this.flowForm)
        this.title = '编辑流程信息'
        this.nodeDrawer = 'WorkflowForm'
      })

      // 点击边
      eventCenter.on('edge:click', (args) => {
        const data = this.lf.getGraphData()
        const exclusiveGateway = data['bpmn:definitions']['bpmn:process'].exclusiveGateway
        // 只有从条件判断节点出来的线才能设置
        if (exclusiveGateway && args.data.sourceNodeId === exclusiveGateway['-id']) {
          this.title = '编辑条件判断'
          this.nodeDrawer = 'lineForm'
          // 先切换右侧的表单，再重新获取边的最新数据，为了兼容从条件判断节点直接点击边的情况
          this.lineDrawer = 'lineForm'
          setTimeout(() => {
            const edgeModel = this.lf.getEdgeModelById(args.data.id)
            const edgeData = edgeModel.getData()
            edgeData.properties = this.restoreData(edgeData.properties)
            this.currentNode = { data: edgeData }
          }, 0)
        } else {
          this.currentNode = new canvas(this.flowForm)
          this.title = '编辑流程信息'
          this.nodeDrawer = 'WorkflowForm'
        }
      })

      // 选择审批人
      eventCenter.on('changeData', (args) => {
        const defaultDate = this.lf.getProperties(args.id)
        this.lf.setProperties(args.id, this.parseData(Object.assign(defaultDate, this.memberChange(args))))
      })
    },

    // 转换已选审批人的数据格式
    memberChange(data) {
      const temArr = (data.memberList || []).map((el) => el.value)
      const temNameArr = (data.memberList || []).map((el) => el.label)
      const param = {}
      if (data.personLiableType === 0) {
        param.candidateUsers = temArr.join(',')
        param.candidateUsersShow = temArr.join(',')
        param.candidateUsersName = temNameArr.join(',')
        param.candidateGroups = ''
        param.candidateGroupsShow = ''
        param.candidateGroupsName = ''
      } else {
        param.candidateGroups = temArr.join(',')
        param.candidateGroupsShow = temArr.join(',')
        param.candidateGroupsName = temNameArr.join(',')
        param.candidateUsers = ''
        param.candidateUsersShow = ''
        param.candidateUsersName = ''
      }
      return param
    },
    // 保存节点属性数据
    saveNode(data, id) {
      const tempData = cloneDeep(data)
      if (tempData.conditionExpression) {
        const edgeModel = this.lf.getEdgeModelById(id)
        if (!tempData.conditionExpression.conditionValue) {
          tempData.name = tempData.name || ''
          tempData.conditionExpression.conditionValue = tempData.conditionExpression.conditionValue || ''
          edgeModel?.updateText('请配置')
        } else {
          edgeModel?.updateText(data.name)
        }
      }
      if (tempData.conditionType || tempData.conditionType === 0) {
        // if (tempData.conditionType !== this.oldGetwayData.data.properties.conditionType) {
        this.updateLine(tempData, id)
        // }
        // 因为后端流程引擎不支持条件流转节点扩展自定义节点，所以将条件流转类型的值拼接到节点名称上
        if (!tempData.name.includes('-')) {
          tempData.name = tempData.name + '-' + tempData.conditionType
        } else {
          const tempName = tempData.name.split('-')[0]
          tempData.name = tempName + '-' + tempData.conditionType
        }
      }
      if (!id) {
        this.flowForm = tempData
        return
      }
      if (tempData.hasOwnProperty('memberList')) {
        delete tempData.memberList
      }
      this.lf.setProperties(id, this.parseData(tempData))
      const nodeModel = this.lf.getNodeModelById(id)
      // const properties = nodeModel.getProperties()
      // console.log('properties', properties)
    },
    //如果是条件判断节点，需要根据节点的选项，设置边的流转条件
    updateLine(data, id) {
      const nodeModel = this.lf.getNodeModelById(id)
      const edgesList = nodeModel?.outgoing.edges ?? []
      edgesList.forEach((edgeItem) => {
        const properties = edgeItem.getProperties()
        const edgeModel = this.lf.getEdgeModelById(edgeItem.id)
        if (data.conditionType === 0) {
          // 如果上次条件流转类型为1，则表示切换了，需要将连接线置空
          if (this.oldGetwayData.data.properties.conditionType === 1) {
            this.lf.setProperties(edgeItem.id, {
              ...properties,
              name: '',
              'flowable:feild': '',
              conditionExpression: { liu: {}, conditionValue: '', 'xsi:type': 'tFormalExpression' }
            })
          }
          const newProperties = edgeItem.getProperties()
          if (!newProperties.conditionExpression?.conditionValue) {
            edgeModel?.updateText('请配置')
          }
        }
        if (data.conditionType === 1) {
          const nodeId = 'nodeId =="' + edgeItem.targetNodeId + '"'
          const newProperties = {
            ...properties,
            'flowable:feild': '',
            conditionExpression: {
              liu: {},
              conditionValue: '${' + nodeId + '}',
              'xsi:type': 'tFormalExpression'
            }
          }
          if (this.oldGetwayData.data.properties.conditionType === 0) {
            edgeModel?.updateText('')
            newProperties.name = ''
          } else if (!properties.name) {
            edgeModel?.updateText('')
          } else {
            edgeModel?.updateText(properties.name)
          }
          this.lf.setProperties(edgeItem.id, newProperties)
        }
      })
    },
    // 节点属性数据转换(后端需要带flowable:前缀)
    parseData(data) {
      const defaultAttr = ['id', 'name', 'documentation', 'multiInstanceLoopCharacteristics', 'conditionExpression']
      const nData = {}
      if (Object.keys(data).length > 0) {
        for (const key in data) {
          if (!defaultAttr.includes(key)) {
            nData[key.includes('flowable:') ? key : 'flowable:' + key] = data[key]
          } else {
            nData[key] = data[key]
          }
        }
      }
      return nData
    },
    // 节点属性数据恢复(前端需要去掉flowable:前缀)
    restoreData(data) {
      const nData = {}
      if (Object.keys(data).length > 0) {
        for (const key in data) {
          nData[key.includes('flowable:') ? key.replace('flowable:', '') : key] = data[key]
        }
      }
      return nData
    }
  }
}
</script>

<style lang="less" scoped>
.flowCanvas {
  position: relative;
  width: 100%;
  height: calc(100vh - 116px);
  margin: 0px;
  display: flex;
}
.flowCanvas .container {
  display: flex;
  flex-grow: 1; /*铺满剩余空间*/
  // border: 1px solid #ccc;
  // box-shadow: 0px 0px 5px 0px #ccc;
  overflow: hidden;
  border-radius: 12px;
}
.btn-content {
  width: 389px;
  margin-top: 10px;
  padding: 0 10px 10px;
  background-color: #fff;
  position: absolute;
  border-radius: 0 0 12px 12px;
  border-top: 1px solid #eee;
  bottom: 0;
  right: 0;
}
.btn-group {
  margin-top: 10px;
  .ant-btn {
    margin-right: 10px;
  }
}
</style>
